/**
 * Copyright (C) 2013 DaiKit.com - daikit4gxt module (admin@daikit.com)
 *
 *         Project home : http://code.daikit.com/daikit4gxt
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.daikit.daikit4gxt.client.ui.forms;

import java.util.ArrayList;
import java.util.List;

import com.daikit.commons.shared.exception.DkUnsupportedMethodException;
import com.daikit.daikit4gxt.client.DkMain;
import com.daikit.daikit4gxt.client.log.BaseLogger;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.core.client.dom.ScrollSupport.ScrollMode;
import com.sencha.gxt.widget.core.client.Component;
import com.sencha.gxt.widget.core.client.container.Container;
import com.sencha.gxt.widget.core.client.container.FlowLayoutContainer;
import com.sencha.gxt.widget.core.client.container.MarginData;
import com.sencha.gxt.widget.core.client.event.AddEvent;
import com.sencha.gxt.widget.core.client.event.AddEvent.AddHandler;
import com.sencha.gxt.widget.core.client.form.FieldLabel;
import com.sencha.gxt.widget.core.client.form.FormPanel.LabelAlign;


/**
 * A simple form panel
 * 
 * @author tcaselli
 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
 */
public class MyFixedFormPanel implements IsWidget
{
	/**
	 * Default label align
	 */
	public final static LabelAlign DEFAULT_LABEL_ALIGN = LabelAlign.LEFT;

	private final int marginVeryLeft = 8;
	private final int marginVeryRight = 8;
	private final int marginVeryTop = 8;

	private final int marginBottom = 5;
	private final int marginRight = 15;

	private int labelWidth;
	private int columnWidth;
	private int columnWrapWidth;

	private static final int x = 56;

	private boolean scrollable = true;
	private int originalTotalWidth;

	private LabelAlign labelAlign;

	private final FlowLayoutContainer widget;
	private HorizontalPanel currentHorizontalPanel;

	private int numberOfColumns = 1;

	private final List<Component> formComponents = new ArrayList<Component>();

	/**
	 * Constructor
	 * 
	 * @param numberOfColumns
	 */
	public MyFixedFormPanel(final int numberOfColumns)
	{
		this(numberOfColumns, DkMain.config().getApplicationWidth());
	}

	/**
	 * Constructor
	 * 
	 * @param numberOfColumns
	 *           the number of columns
	 * @param totalWidth
	 *           total panel width
	 */
	public MyFixedFormPanel(final int numberOfColumns, final int totalWidth)
	{
		widget = new FlowLayoutContainer();
		widget.addStyleName("fill-this-background-color");
		widget.setScrollMode(ScrollMode.AUTOY);
		this.numberOfColumns = numberOfColumns;
		labelWidth = numberOfColumns < 3 ? DkMain.config().getFormLabelWidth() : DkMain.config()
				.getFormMoreThan3ColumnsLabelWidth();
		labelAlign = DEFAULT_LABEL_ALIGN;
		widget.addAddHandler(new ChangeLabelPropsAddHandler());
		setWidth(totalWidth);
	}

	/**
	 * This method must only be called before adding any field to the form panel.
	 * 
	 * @param totalWidth
	 *           the total width
	 */
	public void setWidth(final int totalWidth)
	{
		if (widget.getWidgetCount() > 0)
		{
			throw new DkUnsupportedMethodException("This method must only be called before adding any field to the form panel.");
		}
		widget.setWidth(totalWidth);
		this.columnWrapWidth = getWidthFor1Column(totalWidth);
		this.columnWidth = numberOfColumns == 1 ? columnWrapWidth : getWidthForNColumns(totalWidth, numberOfColumns);
		this.originalTotalWidth = totalWidth;
	}

	/**
	 * set to false if you are sure there will be no scroll bar to be displayed, default is false.
	 * 
	 * @param scrollable
	 */
	public void setKeepScrollableWidth(final boolean scrollable)
	{
		this.scrollable = scrollable;
		setWidth(originalTotalWidth);
	}

	private int getWidthFor1Column(final int totalWidth)
	{
		return totalWidth - x + (scrollable ? 0 : 25);
	}

	private int getWidthForNColumns(final int totalWidth, final int numberOfColumns)
	{
		return Double.valueOf(
				getWidthFor1Column(totalWidth) / Double.valueOf(numberOfColumns) - marginRight / Double.valueOf(numberOfColumns))
				.intValue();
	}

	@Override
	public Widget asWidget()
	{
		return widget;
	}

	/**
	 * @return this popup as a {@link Component}
	 */
	public Component asComponent()
	{
		return widget;
	}

	private void createNewHorizontalPanel()
	{
		currentHorizontalPanel = new HorizontalPanel();
		widget.add(currentHorizontalPanel, new MarginData(widget.getWidgetCount() == 0 ? marginVeryTop : 0, marginVeryRight, 0,
				marginVeryLeft));
		for (int i = 0; i < numberOfColumns; i++)
		{
			final FlowLayoutContainer column = new FlowLayoutContainer();
			column.setWidth(columnWidth);
			if (i < numberOfColumns - 1)
			{
				column.getElement().getStyle().setMarginRight(marginRight, Unit.PX);
			}
			currentHorizontalPanel.add(column);
			column.addAddHandler(new ChangeLabelPropsAddHandler());
		}
	}

	/**
	 * Set the same label width for all children {@link FieldLabel} no matter at which level of sub hierarchy they are
	 * added
	 * 
	 * @author tcaselli
	 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
	 */
	private class ChangeLabelPropsAddHandler implements AddHandler
	{
		@Override
		public void onAdd(final AddEvent event)
		{
			final Widget addedWidget = event.getWidget();
			if (addedWidget instanceof FieldLabel)
			{
				final FieldLabel label = (FieldLabel) addedWidget;
				if (labelWidth > 0)
				{
					label.setLabelWidth(labelWidth);
				}
				if (label.getWidget() instanceof Component)
				{
					final Component labeLWidget = (Component) label.getWidget();
					labeLWidget.setWidth(columnWidth - labelWidth - label.getLabelPad());
				}
				label.setLabelAlign(labelAlign);
				// final Widget widget = label.getWidget() ;
			}
			else if (addedWidget instanceof Container)
			{
				final Container container = (Container) addedWidget;
				container.addAddHandler(new ChangeLabelPropsAddHandler());
			}
		}
	}

	/**
	 * 
	 */
	public void addNewRow()
	{
		currentHorizontalPanel = null;
	}

	/**
	 * Add a child widget to this widget with custom margins
	 * 
	 * @param component
	 */
	public void add(final Component component)
	{
		component.addStyleName("fixedform-component");
		component.getElement().getStyle().setPosition(Position.RELATIVE);
		currentHorizontalPanel = null;
		component.setWidth(columnWrapWidth);
		this.widget.add(component, new MarginData(widget.getWidgetCount() == 0 ? marginVeryTop : 0, marginVeryRight, marginBottom,
				marginVeryLeft));
		formComponents.add(component);
	}

	/**
	 * Add the given component in given column with no margin
	 * 
	 * @param component
	 *           the component to be added
	 * @param columnIndex
	 *           starting from 0 and should be strictly less than this.numberOfColumns
	 */
	public void add(final Component component, final int columnIndex)
	{
		add(component, columnIndex, false);
	}

	/**
	 * Add the given component in given column
	 * 
	 * @param component
	 *           the component to be added
	 * @param columnIndex
	 *           starting from 0 and should be strictly less than this.numberOfColumns
	 * @param addMargins
	 *           to indicate whether to add margins or not
	 */
	public void add(final Component component, final int columnIndex, final boolean addMargins)
	{
		component.addStyleName("fixedform-component");
		if (numberOfColumns > 1 && columnIndex >= 0 && columnIndex < numberOfColumns)
		{
			if (currentHorizontalPanel == null)
			{
				createNewHorizontalPanel();
			}
			final FlowLayoutContainer flc = (FlowLayoutContainer) currentHorizontalPanel.getWidget(columnIndex);
			flc.add(component, addMargins ? new MarginData(widget.getWidgetCount() == 0 ? marginVeryTop : 0, 0, marginBottom, 0)
					: null);
			formComponents.add(component);
		}
		else if (numberOfColumns == 1 && columnIndex == 0)
		{
			add(component);
			formComponents.add(component);
		}
		else
		{
			BaseLogger.error(getClass(), "inconsistency in number of column and given column index : numberOfColumns="
					+ numberOfColumns + ", columnIndex=" + columnIndex);
		}
	}

	/**
	 * @return the formComponents
	 */
	public List<Component> getFormComponents()
	{
		return formComponents;
	}

	/**
	 * @param labelWidth
	 *           the labelWidth to set
	 */
	public void setLabelWidth(final int labelWidth)
	{
		this.labelWidth = labelWidth;
	}

	/**
	 * 
	 * @param labelAlign
	 *           the {@link LabelAlign}
	 */
	public void setLabelAlign(final LabelAlign labelAlign)
	{
		this.labelAlign = labelAlign;
	}
}
